package com.csw.android.ffmpegtest.media.play

import android.content.Context
import android.media.*
import androidx.lifecycle.LifecycleOwner
import com.csw.android.dev_utils.ex.CloseableEx.closeIgnoreException
import com.csw.android.dev_utils.utils.LogUtils
import com.csw.android.ffmpegtest.app.MyApplication
import com.csw.android.ffmpegtest.media.base.AACADTSFrame
import com.csw.android.ffmpegtest.media.base.AACHelper
import com.csw.android.ffmpegtest.media.play.base.BasePlayer
import com.csw.android.ffmpegtest.media.play.base.IPlayerListener
import java.io.FileInputStream

class AACPlayer(fp: String, lifecycleOwner: LifecycleOwner, listener: IPlayerListener) :
    BasePlayer(fp, lifecycleOwner, listener) {
    private var startTime: Long = 0
    private var duration: Long = 0
    private val playThread = PlayThread()
    private val runningPost = object : Runnable {
        override fun run() {
            onRunning(duration, System.currentTimeMillis() - startTime)
            if (working) {
                mainHandler.postDelayed(this, 1000 / 60)
            }
        }
    }

    override fun start(): Boolean {
        if (super.start()) {
            working = true
            playThread.start()
            onStart()
            return true
        }
        return false
    }

    override fun stop(): Boolean {
        if (super.stop()) {
            working = false
            onComplete()
            return true
        }
        return false
    }

    override fun release() {
        super.release()
        working = false
        mainHandler.removeCallbacks(runningPost)
        playThread.release()
    }

    private inner class PlayThread : Thread() {
        private var fileInputStream = FileInputStream(getFilePath())
        private var audioTrack: AudioTrack? = null
        private var mediaCodec: MediaCodec? = null
        private val mediaExtractor = MediaExtractor()
        private var streamEnd = false
        private var callback = object : MediaCodec.Callback() {
            override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {
                LogUtils.i(this@AACPlayer, "onInputBufferAvailable------------------")
                //请求输入一帧数据
                if (working && !streamEnd) {
//                    val presentationTimeUs = (System.currentTimeMillis() - startTime) * 1000
                    codec.getInputBuffer(index)?.run {
                        clear()
                        val len = mediaExtractor.readSampleData(this, 0)
                        LogUtils.d(this@AACPlayer, "onInputBufferAvailable_>$len")
                        if (len == -1) {
                            streamEnd = true
                            //读取不到帧了，标记流已经结束
                            codec.queueInputBuffer(
                                index,
                                0,
                                0,
                                0,
                                MediaCodec.BUFFER_FLAG_END_OF_STREAM
                            )
                        } else {
                            codec.queueInputBuffer(
                                index,
                                0,
                                len,
                                mediaExtractor.sampleTime,//读取当前帧数据的播放时间点
                                0
                            )
                            mediaExtractor.advance()
                        }
//                        val buffer = ByteArray(limit())
//                        val len = fileInputStream.read(buffer)
//                        LogUtils.d(this@AACPlayer, "onInputBufferAvailable_>$len")
//                        if (len == -1) {
//                            //读取不到帧了，标记流已经结束
//                            codec.signalEndOfInputStream()
//                        } else {
//                            put(buffer, 0, len)
//                            codec.queueInputBuffer(
//                                index,
//                                0,
//                                len,
//                                0,
//                                0
//                            )
//                        }
                    }
                }
            }

            override fun onOutputBufferAvailable(
                codec: MediaCodec,
                index: Int,
                info: MediaCodec.BufferInfo
            ) {
                if (working) {
                    if (index >= 0) {
                        //读取数据
                        val outputBuffer = codec.getOutputBuffer(index) ?: return
                        if (info.flags.and(MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                            info.size = 0
                        }
                        if (info.size != 0) {
                            outputBuffer.position(info.offset)
                            outputBuffer.limit(info.offset + info.size)
                            LogUtils.d(this@AACPlayer, "onOutputBufferAvailable__>${info.size}")
                            audioTrack?.write(outputBuffer, info.size, AudioTrack.WRITE_BLOCKING)
                        }
                        outputBuffer.clear()
                        codec.releaseOutputBuffer(index, false)
                        if (info.flags.and(MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                            //所有解码工作已经完成，数据已经输入audioTrack播放
                            onComplete()
                        }
                    }
                }
            }

            override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
                onError(e)
            }

            override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {
            }
        }

        override fun run() {
            super.run()
            var fis: FileInputStream? = null
            try {
//                duration = AACHelper.getAACDuration(getFilePath())
//                if (duration == 0L) {
//                    onError(Exception("未能获取aac文件时长"))
//                    return
//                }
                startTime = System.currentTimeMillis()
                fis = FileInputStream(getFilePath()).apply {
                    AACHelper.readFrameData(this)?.let { it ->
                        ensureAudioTrack(it)
                        ensureMediaCodec(it)
                    }
                }
                mainHandler.post(runningPost)
            } catch (e: Exception) {
                onError(e)
            } finally {
                fis.closeIgnoreException()
            }
        }

        fun release() {
            mediaExtractor.release()
            audioTrack?.release()
            mediaCodec?.release()
            fileInputStream.closeIgnoreException()
        }

        private fun ensureMediaCodec(frame: AACADTSFrame) {
            mediaCodec?.run {
                return
            }
//            val mime = MediaFormat.MIMETYPE_AUDIO_AAC
//            val sampleRateInHz = frame.aacADTSHeader.getSampleRateInHz()
//            val channelConfig = if (frame.aacADTSHeader.channelConfiguration == 2) {
//                AudioFormat.CHANNEL_OUT_STEREO
//            } else {
//                AudioFormat.CHANNEL_OUT_MONO
//            }
//            val audioFormat = AudioFormat.ENCODING_PCM_16BIT
//            val bufferSize =
//                AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)
//            //audio/mp4a-latm
//            val mediaFormat = MediaFormat.createAudioFormat(
//                MediaFormat.MIMETYPE_AUDIO_AAC,
//                sampleRateInHz,
//                frame.aacADTSHeader.channelConfiguration
//            ).apply {
//                setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC)
//                setInteger(
//                    MediaFormat.KEY_AAC_PROFILE,
//                    frame.aacADTSHeader.profile + 1
//                )
//                //比特率，音频文件每秒多少bit，44100hz的最小56000 最大256000
//                setInteger(
//                    MediaFormat.KEY_BIT_RATE, 256000
//                )
//                setInteger(
//                    MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize * 2
//                )
//                setInteger(MediaFormat.KEY_IS_ADTS, 1)
//                //ByteBuffer key（暂时不了解该参数的含义，但必须设置）
//                val csd_0 = ByteBuffer.wrap(frame.aacADTSHeader.getESDS())
//                setByteBuffer("csd-0", csd_0)
//            }

            //通过mediaExtractor加载文件，他自动将媒体文件解封装，遍历选取我们要的音轨，取得mediaFormat用于
            //配置mediaCodec进行数据解码，解析数据采集通过mediaExtractor进行读取，他解析文件，获取音频帧，
            //并生成每帧对应的时间点，就不用我们自己去计算了
            var mediaFormat: MediaFormat? = null
            var mime: String = MediaFormat.MIMETYPE_AUDIO_AAC
            var hasAudioTrack = false
            mediaExtractor.apply {
                setDataSource(getFilePath())
                for (i in 0 until trackCount) {
                    mediaFormat = getTrackFormat(i)
                    val mimeType = mediaFormat?.getString(MediaFormat.KEY_MIME)
                    if (mimeType != null && mimeType.startsWith("audio/")) {
                        mime = mimeType
                        selectTrack(i)
                        hasAudioTrack = true
                        break
                    }
                }
            }
            if (hasAudioTrack) {
                //读取文件播放时间
                mediaFormat?.getLong(MediaFormat.KEY_DURATION)?.run {
                    duration = this / 1000
                }
                mediaCodec =
                    MediaCodec.createDecoderByType(mime).apply {
                        configure(mediaFormat, null, null, 0)
                        //通过回调的方式放入数据进行编码和取数据进行存储
                        setCallback(callback)
                        start()
                    }
            } else {
                throw RuntimeException("no audio track")
            }
        }

        private fun ensureAudioTrack(frame: AACADTSFrame): AudioTrack {
            audioTrack?.run {
                return this
            }
            val audioManager =
                MyApplication.instance.getSystemService(Context.AUDIO_SERVICE) as AudioManager
            val sessionId = audioManager.generateAudioSessionId()
            val sampleRateInHz = frame.aacADTSHeader.getSampleRateInHz()
            val channelConfig = if (frame.aacADTSHeader.channelConfiguration == 2) {
                AudioFormat.CHANNEL_OUT_STEREO
            } else {
                AudioFormat.CHANNEL_OUT_MONO
            }
            val audioFormat = AudioFormat.ENCODING_PCM_16BIT
            val bufferSize =
                AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)
            val newInstance = AudioTrack(
                AudioAttributes.Builder()
                    .setLegacyStreamType(AudioManager.STREAM_MUSIC)
                    .build(),
                AudioFormat.Builder()
                    .setChannelMask(channelConfig)
                    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                    .setSampleRate(sampleRateInHz)
                    .build(),
                bufferSize,
                AudioTrack.MODE_STREAM,
                sessionId
            )
            audioTrack = newInstance
            newInstance.play()
            return newInstance
        }
    }

}